package gee

import (
	"html/template"
	"log"
	"net/http"
	"strings"
)

type routerGroup struct {
	prefix      string
	middlewares []HandlerFunc
	engine      *engine
}

type engine struct {
	*routerGroup
	groups    []*routerGroup
	router    *router
	templates *template.Template
}

func (g *routerGroup) Group(prefix string) *routerGroup {

	group := &routerGroup{
		prefix:      strings.Join([]string{g.prefix, prefix}, ""),
		middlewares: []HandlerFunc{},
		engine:      g.engine,
	}
	g.engine.groups = append(g.engine.groups, group)

	return group
}

func (g *routerGroup) AddRouter(method string, path string, handler HandlerFunc) {

	g.engine.router.AddRouter(method, strings.Join([]string{g.prefix, path}, ""), handler)
}

func (g *routerGroup) Get(path string, handler HandlerFunc) {

	g.AddRouter(http.MethodGet, path, handler)
}

func (g *routerGroup) Post(path string, handler HandlerFunc) {

	g.AddRouter(http.MethodPost, path, handler)
}

func (g *routerGroup) Use(middlewares ...HandlerFunc) {
	g.middlewares = append(g.middlewares, middlewares...)
}

func (g *routerGroup) Static(relativePath, localPath string) {

	dir := http.Dir(localPath)
	fullRelativePath := strings.Join([]string{g.prefix, relativePath}, "")
	fs := http.StripPrefix(fullRelativePath, http.FileServer(dir))

	pathPattern := strings.Join([]string{relativePath, "/*file"}, "")
	g.Get(pathPattern, func(c *Context) {

		file := c.Param("file")
		if _, err := dir.Open(file); err != nil {
			c.SetStatusCode(http.StatusNotFound)
			c.HTML("Static Not Found")
			return
		}

		fs.ServeHTTP(c.Resp, c.Req)
	})
}

func New() *engine {

	e := &engine{router: NewRouter()}
	e.routerGroup = &routerGroup{
		prefix:      "",
		middlewares: []HandlerFunc{},
		engine:      e,
	}

	e.Get("/favicon.ico", func(c *Context) {
		c.SetHeader("Content-Type", "image/gif")
		c.Data([]byte(""))
	})
	return e
}

func (e *engine) Run(addr string) {

	log.Printf("Start server at %s...\n", addr)
	err := http.ListenAndServe(addr, e)
	if err != nil {
		log.Fatalf("Start server failed: %s\n", err.Error())
	}
}

func (e *engine) LoadTemplates(pattern string, funcMap template.FuncMap) {

	e.templates = template.Must(template.New("").Funcs(funcMap).ParseGlob(pattern))
}

func pathMatchPrefix(path, prefix string) bool {

	pathLen := len(path)
	prefixLen := len(prefix)
	if pathLen < prefixLen {
		return false
	}
	if path[:prefixLen] == prefix && (prefixLen == pathLen || path[prefixLen:prefixLen+1] == "/") {
		return true
	}
	return false
}

func (e *engine) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

	c := NewContext(rw, r)

	c.engine = e

	// copy middlewares to c
	c.handlers = append(c.handlers, e.middlewares...)
	for i := 0; i < len(e.groups); i++ {
		if pathMatchPrefix(r.URL.Path, e.groups[i].prefix) {
			c.handlers = append(c.handlers, e.groups[i].middlewares...)
		}
	}

	e.router.Handle(c)
}
